﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

namespace DependencyAnalysis.Serialization
{
    [Serializable]
    public class XmlSerializableExpandoObject : DynamicObject, IXmlSerializable, IReportObject
    {
        private readonly Dictionary<string, object> _properties = new Dictionary<string, object>();

        public string Name { get; set; }

        public string TypeName { get; set; }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            foreach (var pair in _properties)
            {
                if (pair.Value == null) continue;
                info.AddValue(pair.Key, pair.Value);
            }
        }

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            _properties.TryGetValue(binder.Name, out result);
            return true;
        }

        public void Set(string key, object value)
        {
            _properties[key] = value;
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            _properties[binder.Name] = value;
            return true;
        }

        public XmlSchema GetSchema()
        {
            throw new NotImplementedException();
        }

        public void ReadXml(XmlReader reader)
        {
            throw new NotImplementedException();
        }

        public void WriteXml(XmlWriter writer)
        {
            writer.WriteStartAttribute("Name");
            writer.WriteValue(Name);
            writer.WriteEndAttribute();
            writer.WriteStartAttribute("TypeName");
            writer.WriteValue(TypeName);
            writer.WriteEndAttribute();
            foreach (var pair in _properties)
            {
                if (pair.Value == null) continue;

                var xmlSerializable = pair.Value as XmlSerializableExpandoObject;
                var enumerable = pair.Value is string ? null : pair.Value as IEnumerable;
                if (xmlSerializable != null)
                {
                    writer.WriteStartElement(pair.Key);
                    xmlSerializable.WriteXml(writer);
                    writer.WriteEndElement();
                }
                else if (enumerable != null)
                {
                    writer.WriteStartElement(pair.Key);
                    foreach (XmlSerializableExpandoObject item in enumerable)
                    {
                        writer.WriteStartElement(item.TypeName);
                        item.WriteXml(writer);
                        writer.WriteEndElement();
                    }
                    writer.WriteEndElement();
                }
                else
                {
                    var iXmlSerializable = pair.Value as IXmlSerializable;
                    writer.WriteStartElement(pair.Key);
                    if (iXmlSerializable == null)
                    {
                        writer.WriteValue(pair.Value);
                    }
                    else
                    {
                        iXmlSerializable.WriteXml(writer);
                    }
                    writer.WriteEndElement();
                }
            }
        }
    }
}